module lang.tokenset;

import utils.log;
import std.algorithm;

class TokenSet {
    private static shared const TokenSet EMPTY;

    static this() {
        EMPTY = new shared(TokenSet)(0);
    }

    private bool[] mySet;

    private this(in uint typesCount) {
        mySet = new bool[typesCount];
    }

    public static const(shared(TokenSet)) create(TokenType)(TokenType[] types ...) {
        if (types.length == 0) {
            return EMPTY;
        }
        auto tokenSet = new shared(TokenSet)(TokenType.max + 1);
        foreach (type; types) {
            tokenSet.mySet[cast(uint) type] = true;
        }
        return tokenSet;
    }

    public static const(shared(TokenSet)) orSet(shared(const(TokenSet))[] sets...) shared {
        if (sets.length == 0) {
            return EMPTY;
        }

        uint a = 0;
        foreach (set; sets) {
            if (set.mySet.length > a) {
                a = set.mySet.length;
            }
        }
        auto newSet = new shared(TokenSet)(a);

        foreach (set; sets) {
            foreach (i, elem; set.mySet) {
                newSet.mySet[i] |= elem;
            }
        }
        return newSet;
    }

    // COMPILER_BUG_3682: IsExpression fails to match type
    public static const(shared(TokenSet)) andSet(TokenSet[] sets...) shared {
        if (sets.length == 0) {
            return EMPTY;
        }
        auto newSet = new shared(TokenSet)(0/*minCount!((TokenSet a, TokenSet b) {
            return a.mySet.length < b.mySet.length;
        })(sets).field[0].mySet.length*/);

        foreach (set; sets) {
            foreach (i, ref elem; newSet.mySet) {
                elem &= set.mySet[i];
            }
        }
        return newSet;
    }

    public bool opIn_r(TokenType)(TokenType type) shared const {
        if (type.max != mySet.length - 1) {
            return false;
        }
        return mySet[cast(uint) type];
    }
}
